package tjs.study.notes.dotest.proxy.asm;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import tjs.study.notes.dotest.TjsStudyNotesApplication;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;

public class MyGenerator extends ClassLoader {
    private byte[] testClassBttes;

    public static final String classAbsolutePath = TjsStudyNotesApplication.work_space + "/proxy/asm/TestAsm.class";
    public static final String classReferencePath = "tjs.study.notes.dotest.proxy.asm.TestAsm";


    public static void main(String[] args) throws Exception {
        MyGenerator myGenerator = new MyGenerator();
        // 构造TestAsm.class
        myGenerator.buildOneClass(classAbsolutePath, classReferencePath);
        // 加载TestAsm.class
        Class<?> testClass = myGenerator.loadClass(classReferencePath);

        // 反射调用目标方法
        Object object = testClass.newInstance();
        Method testMethod = testClass.getDeclaredMethod("doSomething");
        testMethod.invoke(object);
    }

    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        Class clazz = defineClass(className, testClassBttes, 0, testClassBttes.length);
        return clazz;
    }

    private void buildOneClass(String classAbsolutePath, String classReferencePath) throws IOException {
        ClassWriter classWriter = new ClassWriter(0);
        // 通过visit方法确定类的头部信息
        classWriter.visit(Opcodes.V1_7,// java版本
                Opcodes.ACC_PUBLIC,// 类修饰符
                classReferencePath.replaceAll("\\.", "/"), // 类的全限定名  "tjs/study/notes/dotest/proxy/asm/TestAsm"
                null, "java/lang/Object", null);

        //创建构造函数
        MethodVisitor mv = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // 定义code方法
        MethodVisitor methodVisitor = classWriter.visitMethod(Opcodes.ACC_PUBLIC, "doSomething", "()V",
                null, null);
        methodVisitor.visitCode();
        methodVisitor.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
                "Ljava/io/PrintStream;");
        methodVisitor.visitLdcInsn("测试asm动态创建字节码数组。。。");
        methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                "(Ljava/lang/String;)V");
        methodVisitor.visitInsn(Opcodes.RETURN);
        methodVisitor.visitMaxs(2, 2);
        methodVisitor.visitEnd();
        classWriter.visitEnd();

        byte[] data = classWriter.toByteArray();
        this.testClassBttes = data;

        // 将classWriter转换成字节数组写到文件里面去
        File file = new File(classAbsolutePath);
        FileOutputStream fout = new FileOutputStream(file);
        fout.write(data);
        fout.close();
    }
}